<?php

/**
 * @desc		抽象类：数据表模型
 * ---------------------------------------------------------------------
 * @author	unphp <unphp@qq.com>
 * @date		2014-03-27
 * @copyright	UnPHP 1.1
 * ---------------------------------------------------------------------
 */

namespace UnPHP\Lib\DBdriver;

abstract class Model extends CommonSql
{

        /**         * */
        protected $_pk         = null;
        protected $_attributes = array();
        protected $_validaObj  = null;
        protected $_error      = array();
        protected $_isNew      = true;
        protected static $_modeSelf = null;

        /**
         * 返回：模型对应的“数据表名称”
         * 抽象方法，子类必须实现的方法
         * @author		liuxiang <unphp@qq.com>
         * @date		2015-04-03
         */
        abstract public function getTableName();

        /**
         * 返回：模型对应的“数据库名称”
         * 抽象方法，子类必须实现的方法
         * @author		liuxiang <unphp@qq.com>
         * @date		2015-04-03
         */
        abstract public function getDbName2();

        public final function getDbName()
        {
                return $this->getDbName2();
        }

        /**
         * 
         * @return ModelSqlParseCreate
         */
        public  function write()
        {
                $w = new ModelSql($this->getDbName());
                return $w->createWriter();
        }

        /**
         * 
         * @param type $attributes
         * @param type $isNew   该参数是针对 save（）的：当用findOne或findAll时，返回了查询结果并实例来对象本身，此时这个参数为False。
         *                       即，当save操作受到isNew值影响：isNew为TRUE时，save实际是insert；isNew为False时，save实际是update。
         */
        public function __construct($attributes = array(), $isNew = TRUE)
        {
                $this->_attributes = $attributes;
                $this->_isNew      = $isNew;
                $this->_error      = array();
                $this->_validaObj  = new Validation($this);
        }

        public function fileds()
        {
                return array();
        }

        public function rules()
        {
                return array(
                );
        }

        public function validation($data)
        {
                $rs    = true;
                $rules = $this->rules();
                if (!empty($rules))
                {
                        foreach ($rules as $rule)
                        {
                                if (is_array($rule) && count($rule) > 1)
                                {
                                        if (isset($this->_validation[$rule[1]]))
                                        {
                                                $valida = $this->_validation[$rule[1]]($data, $rule);
                                                if (true !== $valida)
                                                {
                                                        $this->_error[] = $valida;
                                                        $rs             = false;
                                                }
                                        }
                                        else
                                        {
                                                $valida = $this->_validaObj->$rule[1]($data, $rule);
                                                if (true !== $valida)
                                                {
                                                        $rs = false;
                                                }
                                        }
                                }
                        }
                }
                return $rs;
        }

        public function addError($err)
        {
                $this->_error[] = $err;
        }

        public function getError()
        {
                return $this->_error;
        }
        
        public function getErrors()
        {
                return Pool::getError();
        }
        
        public function traceSql()
        {
                return Pool::getSql();
        }

        public function getPk()
        {
                return Pool::instance()->getWritePool($this->getDbName2())->conn()->getPk($this->getTableName());
        }

        public function getPkId()
        {
                return $this->_pk;
        }

        public function setAttributes($attributes)
        {
                if ($attributes && is_array($attributes))
                {
                        foreach ($attributes as $key => $value)
                        {
                                $this->setAttribute($key, $value);
                        }
                }
        }

        public function setAttribute($key, $value, $type = null)
        {
                if ($this->_isNew)
                {
                        $this->_attributes[$key] = $value;
                }
                else
                {
                        if ($type == 'inc')
                        {
                                $this->_new['$inc'][$key] = $value;
                        }
                        else
                        {
                                $this->_new['$set'][$key] = $value;
                        }
                }
                return $this;
        }

        public function getAttributes()
        {
                return $this->_attributes;
        }

        public function getOne(ParseQuery $parse)
        {
                $data = $this->findOne($parse);
                if ($data)
                {
                        return $data->getAttributes();
                }
                else
                {
                        return FALSE;
                }
        }

        public function getAll(ParseQuery $parse)
        {
                $datas = $this->findAll($parse);
                if ($datas)
                {
                        $data = array();
                        foreach ($datas as $obj)
                        {
                                $data[] = $obj->getAttributes();
                        }
                        unset($datas);
                        return $data;
                }
                else
                {
                        return FALSE;
                }
        }

        public function newParse()
        {
                return new ParseQuery();
        }

        public function findOne(ParseQuery $parse)
        {
                $condition = $parse->getQueryCondition();
                $options   = array();
                $sort      = $parse->getQueryOrder();
                $fileds    = $parse->getQueryFileds();
                if (!empty($sort))
                {
                        $options['sort'] = $parse->getQueryOrder();
                }
                if (!empty($fileds))
                {
                        $options['fields'] = $parse->getQueryFileds();
                }
                $data = Pool::instance()
                        ->getReadPool($this->getDbName2())
                        ->conn()
                        ->findOne($this->getTableName(), $condition, $options);
                if ($data)
                {
                        $className = get_called_class();
                        return new $className($data, FALSE);
                }
                return false;
        }

        public function findAll(ParseQuery $parse)
        {
                $condition = $parse->getQueryCondition();
                $options   = array();
                $sort      = $parse->getQueryOrder();
                $fileds    = $parse->getQueryFileds();
                $offset    = $parse->getQueryOffset();
                $limit     = $parse->getQueryLimit();
                if (!empty($sort))
                {
                        $options['sort'] = $sort;
                }
                if (!empty($fileds))
                {
                        $options['fields'] = $fileds;
                }
                if (null !== $offset && null !== $limit)
                {
                        $options['offset'] = $offset;
                        $options['limit']  = $limit;
                }
                $data = Pool::instance()
                        ->getReadPool($this->getDbName2())
                        ->conn()
                        ->findAll($this->getTableName(), $condition, $options);
                if ($data)
                {
                        $rs        = array();
                        $className = get_called_class();
                        foreach ($data as $value)
                        {
                                $rs[] = new $className($value, FALSE);
                        }
                        return $rs;
                }
                return false;
        }

        public function count(ParseQuery $parse)
        {
                $condition = $parse->getQueryCondition();
                return Pool::instance()
                                ->getReadPool($this->getDbName2())
                                ->conn()
                                ->count($this->getTableName(), $condition);
        }

        public function remove(ParseQuery $parse)
        {
                $condition = $parse->getQueryCondition();
                $options   = array();
                return Pool::instance()->getWritePool($this->getDbName2())
                                ->conn()
                                ->remove($this->getTableName(), $condition, $options);
        }

        private function _insert($new, $options = array())
        {
                $data = false;
                if ($this->validation($new))
                {
                        $this->_pk = Pool::instance()->getWritePool($this->getDbName2())
                                ->conn()
                                ->insert($this->getTableName(), $new, $options);
                        if ($this->_pk)
                        {
                                $this->_isNew = FALSE;
                                return $this;
                        }
                }
                return $data;
        }

//    public function batchInsert($new, $options = array())
//    {
//        return Pool::instance()->getWritePool($this->getDbName2())
//                ->conn()
//                ->batchInsert($this->getTableName(), $new, $options);
//    }

        private function _update($condition, $new, $options = array())
        {
                return Pool::instance()->getWritePool($this->getDbName2())
                                ->conn()
                                ->update($this->getTableName(), $condition, $new, $options);
        }

        public function save()
        {
                if ($this->_attributes)
                {
                        if ($this->_isNew)
                        {
                                return $this->_insert($this->getAttributes());
                        }
                        else
                        {
                                $condition                 = array();
                                $condition[$this->getPk()] = $this->_attributes[$this->getPk()];
                                return $this->_update($condition, $this->getAttributes());
                        }
                }
        }

        public function __get($name)
        {
                if (isset($this->_attributes[$name]))
                {
                        return $this->_attributes[$name];
                }
        }

        public function __set($k, $v)
        {
                $this->_attributes[$k] = $v;
        }

}
